package random

import (
	"math/rand"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func YYYY(date time.Time) string {
	return strconv.Itoa(date.Year())
}

func YY(date time.Time) string {
	yy := strconv.Itoa(date.Year())
	return yy[len(yy)-2:]
}

func MM(date time.Time) string {
	var m = int(date.Month())
	return Triad(m < 10, "0"+strconv.Itoa(m), strconv.Itoa(m))
}

func DD(date time.Time) string {
	var d = date.Day()
	return Triad(d < 10, "0"+strconv.Itoa(d), strconv.Itoa(d))
}

func D(date time.Time) string {
	return strconv.Itoa(date.Day())
}

func HH(date time.Time) string {
	h := date.Hour()
	return Triad(h < 10, "0"+strconv.Itoa(h), strconv.Itoa(h))
}

func H(date time.Time) string {
	return strconv.Itoa(date.Hour())
}

func MMI(date time.Time) string {
	m := date.Minute()
	return Triad(m < 10, "0"+strconv.Itoa(m), strconv.Itoa(m))
}

func MI(date time.Time) string {
	return strconv.Itoa(date.Minute())
}

func SS(date time.Time) string {
	s := date.Second()
	return Triad(s < 10, "0"+strconv.Itoa(s), strconv.Itoa(s))
}

func S(date time.Time) string {
	return strconv.Itoa(date.Second())
}

func SSMil(date time.Time) string {
	var ms = date.UnixMilli() % 1000
	return Triad(ms < 10, "00"+strconv.FormatInt(ms, 10),
		Triad(ms < 100, "0"+strconv.FormatInt(ms, 10),
			strconv.FormatInt(ms, 10)),
	)
}

func SMil(date time.Time) string {
	return strconv.FormatInt(date.UnixMilli()%1000, 10)
}

func A(date time.Time) string {
	var h = date.Hour()
	return Triad(h < 12, "AM", "PM")
}

func a(date time.Time) string {
	var h = date.Hour()
	return Triad(h < 12, "am", "pm")
}

func T(date time.Time) string {
	return strconv.FormatInt(date.UnixMilli(), 10)
}

var patternLetters = map[string]func(date time.Time) string{
	"yyyy": YYYY,
	"yy":   YY,
	"y":    YY,
	"MM":   MM,
	"dd":   DD,
	"d":    D,
	"HH":   HH,
	"H":    H,
	"hh":   HH,
	"h":    H,
	"mm":   MMI,
	"m":    MI,
	"ss":   SS,
	"s":    S,
	"SS":   SSMil,
	"S":    SMil,
	"A":    A,
	"a":    a,
	"T":    T, // 毫秒时间戳
}

var patternLettersArr = []string{
	"yyyy", "yy", "y",
	"MM",
	"dd", "d",
	"HH", "H", "hh", "h",
	"mm", "m", "ss", "s",
	"SS", "S",
	"A", "a", "T",
}

func _rformat() *regexp.Regexp {
	var re []string
	for _, name := range patternLettersArr {
		re = append(re, name)
	}
	reArrStr := "(" + strings.Join(re, "|") + "|-|.|:| )"
	return regexp.MustCompile(reArrStr)
}

var _rformatReg = _rformat()

func FormatDate(date time.Time, format string) string {
	flags := _rformatReg.FindAllString(format, -1)

	var v []string
	for _, flag := range flags {
		f, ok := patternLetters[string(flag)]
		if ok {
			v = append(v, f(date))
		} else {
			v = append(v, string(flag))
		}
	}
	return strings.Join(v, "")
}

func _randomDate(minUnixMs, maxUnixMs int64) time.Time {
	if minUnixMs <= 0 {
		minUnixMs = 0
	}
	if maxUnixMs <= 0 {
		maxUnixMs = time.Now().UnixMilli()
	}
	unixMs := int64(rand.Float64()*float64(maxUnixMs-minUnixMs)) + minUnixMs
	// 秒级时间
	return time.UnixMilli(unixMs)
}

func Date(format string) string {
	format = Triad(len(format) == 0, "yyyy-MM-dd", format)
	return FormatDate(_randomDate(0, 0), format)
}

func Time(format string) string {
	format = Triad(len(format) == 0, "HH:mm:ss", format)
	return FormatDate(_randomDate(0, 0), format)
}

func Datetime(format string) string {
	format = Triad(len(format) == 0, "yyyy-MM-dd HH:mm:ss", format)
	return FormatDate(_randomDate(0, 0), format)
}

func Now(unit, format string) string {
	format = Triad(len(format) == 0, "yyyy-MM-dd HH:mm:ss", format)
	var date = time.Now()
	switch unit {
	case "year":
		date = time.Date(date.Year(), 0, date.Day(),
			date.Hour(), date.Minute(), date.Second(),
			date.Nanosecond(), date.Location())
	case "month":
		date = time.Date(date.Year(), date.Month(), 1,
			date.Hour(), date.Minute(), date.Second(),
			date.Nanosecond(), date.Location())
	case "week":
		fallthrough
	case "day":
		date = time.Date(date.Year(), date.Month(), date.Day(),
			0, date.Minute(), date.Second(),
			date.Nanosecond(), date.Location())
	case "hour":
		date = time.Date(date.Year(), date.Month(), date.Day(),
			date.Hour(), 0, date.Second(),
			date.Nanosecond(), date.Location())
	case "minute":
		date = time.Date(date.Year(), date.Month(), date.Day(),
			date.Hour(), date.Minute(), 0,
			date.Nanosecond(), date.Location())
	case "second":
		date = time.Date(date.Year(), date.Month(), date.Day(),
			date.Hour(), date.Minute(), date.Second(),
			0, date.Location())
	}
	switch unit {
	case "week":
		date = time.Date(date.Year(), date.Month(), date.Day()-int(date.Weekday()),
			date.Hour(), date.Minute(), date.Second(),
			date.Nanosecond(), date.Location())
	}
	return FormatDate(date, format)
}
